﻿
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using System.Linq;
using System.Web;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

using System.Security.Cryptography;
using System.Net.Http;
using System.Runtime.CompilerServices;
using System.Net.Sockets;
using System.Threading;

using System.Net.Http.Headers;
using System.Net.Mail;
using System.Security.Policy;
using static System.Runtime.CompilerServices.RuntimeHelpers;
using static System.Net.Mime.MediaTypeNames;




/// <summary>
/// 云星辰的单据查询接口V1,友佳建材正在用
/// 这是云星辰V1的api，可以继续使用，但是金蝶官方不在维护，而且调用没有封装。已经跟换为V2接口
/// 仅保留参考。
/// </summary>

namespace GnpFine.Application.NmpaSystem;
public class KdStartOpertionUtils
{

    private string ClientID = "268149";
    private string ClientSecret = "5074b575301cbf8b694e46f90f0e0db5";
    private string outerInstanceId = "227496776318455808";

    private AcctInfo acctInfo = null;


    public KdStartOpertionUtils()
    {
        initToken();
    }


    #region 友佳建材的获取客户和物料基础资料，导入销售出库单。
    // 查询所有客户
    public List<CustModel> GetCustListSum()
    {

        List<CustModel> tmp1 = new List<CustModel>();

        var jSetting = new JsonSerializerSettings();
        jSetting.NullValueHandling = NullValueHandling.Ignore;

        int i = 1;
        while (true)
        {          // 预查寻
            string tmp2 = GetCustList(i);
            if (JObject.Parse(tmp2)["success"].Value<string>().ToUpper().Equals("TRUE"))
            {
                string oo = JObject.Parse(tmp2)["data"]["rows"].ToString();
                tmp1.AddRange(JsonConvert.DeserializeObject<List<CustModel>>(oo, jSetting));

                int sum = JObject.Parse(tmp2)["data"]["totalpage"].Value<int>();
                if (sum == i)
                {
                    break;
                }
            }
            i = i + 1;

        }
        return tmp1;
    }

    public string GetCustList(int page)
    {

        string jsonDate = "{  " +
            "	\"pagesize\":100000,  " +
            "        \"page\":" + page + ",  " +
            "        \"group\":[],  " +
            "        \"startdate\" :\"\" ,  " +
            "        \"enddate\" :\"\" ,  " +
            "        \"starttime\":-1,  " +
            "        \"endtime\":-1,  " +
            "        \"orderby\":\"\",  " +
            "        \"search\":\"\"  " +
            "}  ";




        string url = "http://api.kingdee.com/jdy/basedata/customer_list?access_token=" + acctInfo.accessToken;

        string rt = "";
        rt = postCustApiClient(url, jsonDate);
        return rt;

    }


    /*获取物料*/
    public List<MatModel> GetMatListSum()
    {
        List<MatModel> tmp1 = new List<MatModel>();

        var jSetting = new JsonSerializerSettings();
        jSetting.NullValueHandling = NullValueHandling.Ignore;

        int i = 1;
        while (true)
        {          // 预查寻
            string tmp2 = GetMatList(i);
            if (JObject.Parse(tmp2)["success"].Value<string>().ToUpper().Equals("TRUE"))
            {
                string oo = JObject.Parse(tmp2)["data"]["rows"].ToString();
                tmp1.AddRange(JsonConvert.DeserializeObject<List<MatModel>>(oo, jSetting));

                int sum = JObject.Parse(tmp2)["data"]["totalpage"].Value<int>();
                if (sum == i)
                {
                    break;
                }
            }
            i = i + 1;

        }
        return tmp1;
    }

    public string GetMatList(int page)
    {
        string jsonDate = " {" +
                            "	\"pagesize\":100000,  " +
            "        \"page\":" + page + ",  " +
                "\"search\":\"\"," +
                "\"enable\":\"-1\"," +
                "\"parent\":[]," +
                "\"selectfields\":\"refistrationnumber\"," +
                "\"starttime\":-1," +
                "\"endtime\":-1" +
                "} ";



        string url = "http://api.kingdee.com/jdy/basedata/material_list?access_token=" + acctInfo.accessToken;

        string rt = "";
        rt = postCustApiClient(url, jsonDate);
        return rt;

    }

    // 获取销售出库单------------开发蓝信药监做的供参考
    public string GetKdSaleData1()
    {
        var jSetting = new JsonSerializerSettings();
        jSetting.NullValueHandling = NullValueHandling.Ignore;


        //  一 、获取预查询数据
        string url = "http://api.kingdee.com/jdy/sal/sal_outbound_list?access_token=" + acctInfo.accessToken;
        string jsonprepram = " { " +
            " 	\"deptid\":\"[]\", " +
            " 	\"empid\":[], " +
            " 	\"billstatus\": \"\", " +
            " 	\"settlestatus\":\"\", " +
            " 	\"currencyid\":[], " +
            " 	\"customerid\":[], " +
            // " 	\"startdate\" : \"" + enddate + "\", " +
            // " 	\"enddate\" : \"" + enddate + "\", " +
            "   \"starttime\":-1, " +
            "   \"endtime\":-1, " +
            " 	\"search\":\"\", " +
            " 	\"payment_flag\":-1, " +
            " 	\"iostatus\":\"\", " +
            " 	\"page\":1, " +
            " 	\"pagesize\":1000, " +
            " 	\"selectfields\":\"createtime,customerid.name\", " +
            " 	\"totalfields\":\"totalunsettleamount\", " +
            " 	\"orderby\":\"createtime desc\", " +
            "   \"top\":0 " +
            " } ";

        string rt = "";
        //string rt = HttpPost(url, jsonprepram);

        /*           List<KDPreInstrumentSale> kdpresale = null;
                   if (JObject.Parse(rt)["success"].Value<string>().ToUpper().Equals("TRUE"))
                   {
                       string oo = JObject.Parse(rt)["data"]["rows"].ToString();
                       kdpresale = JsonConvert.DeserializeObject<List<KDPreInstrumentSale>>(oo, jSetting);
                   }*/







        return rt;
    }

    // 保存销售出库单
    public string SaveKdSaleData(string jsonprepram)
    {


        string url = "http://api.kingdee.com/jdy/sal/sal_outbound_save?access_token=" + acctInfo.accessToken;

        string rt = "";
        rt = postCustApiClient(url, jsonprepram);
        return rt;
    }

    #endregion



    public ResponseModel<InstrumentSale> GetKdSaleData(string para, string enddate)
    {
        var jSetting = new JsonSerializerSettings();
        jSetting.NullValueHandling = NullValueHandling.Ignore;

        //  一 、获取预查询数据
        string jsonDate = " { " +
            " 	\"deptid\":\"[]\", " +
            " 	\"empid\":[], " +
            " 	\"billstatus\": \"\", " +
            " 	\"settlestatus\":\"\", " +
            " 	\"currencyid\":[], " +
            " 	\"customerid\":[], " +
            " 	\"startdate\" : \"" + enddate + "\", " +
            " 	\"enddate\" : \"" + enddate + "\", " +
            "   \"starttime\":-1, " +
            "   \"endtime\":-1, " +
            " 	\"search\":\"\", " +
            " 	\"payment_flag\":-1, " +
            " 	\"iostatus\":\"\", " +
            " 	\"page\":1, " +
            " 	\"pagesize\":1000, " +
            " 	\"selectfields\":\"createtime,customerid.name\", " +
            " 	\"totalfields\":\"totalunsettleamount\", " +
            " 	\"orderby\":\"createtime desc\", " +
            "   \"top\":0 " +
            " } ";

        string url = "http://api.kingdee.com/jdy/sal/sal_outbound_list?access_token=" + acctInfo.accessToken;
        List<KDPreInstrumentSale> kdpresale = new List<KDPreInstrumentSale>();
        string rt  = postCustApiClient(url, jsonDate);

        if (JObject.Parse(rt)["success"].Value<string>().ToUpper().Equals("TRUE"))
        {
            string oo = JObject.Parse(rt)["data"]["rows"].ToString();
            kdpresale = JsonConvert.DeserializeObject<List<KDPreInstrumentSale>>(oo, jSetting);
        }



        // 二 、 获取单据明细 并组装数据
        List<InstrumentSale> islist = new List<InstrumentSale>();
        foreach (KDPreInstrumentSale kdpis in kdpresale)
        {
            string jsonpram = " {\"number\":\"" + kdpis.billno + "\"}";
            string oom = postCustApiClient("http://api.kingdee.com/jdy/sal/sal_outbound_detail?access_token=" + accessToken, jsonpram);
            string ooma = JObject.Parse(oom)["data"].ToString();
            KDInstrumentSale kdis = JsonConvert.DeserializeObject<KDInstrumentSale>(ooma, jSetting);

            foreach (KDInstrumentSaleEntity kdise in kdis.material_entity)
            {
                islist.Add(new InstrumentSale()
                {
                    SALE_NO = kdis.billno,
                    SALE_DATE = kdis.billdate,
                    INSTRUMENT_CODE = kdise.materialid_number,
                    INSTRUMENT_NAME = kdise.materialid_name,
                    REGIST_NO = kdise.custom_entity_field__6__22guspuvyz94__materialid__refistrationnumber,
                    BATCH_NO = kdise.batchno,
                    VALIDITY = kdise.validdate,
                    PRODUCER = kdise.custom_entity_field__6__22dyhzdxn4aubl__materialid__custom_field__4__22dyhvyxiew4bixieq,
                    SPEC = kdise.materialid_model,
                    UNIT = kdise.unit_name,
                    QTY = kdise.qty,
                    PURCHASER_NAME = kdis.customerid_name,
                    PURCHASER_CODE = kdpis.customerid_number,
                    DESTROY_NO = ""
                });
            }
        }

        ResponseModel<InstrumentSale> tmp3 = new ResponseModel<InstrumentSale>(false, "失败");
        tmp3.data = islist;
        tmp3.success = true;
        tmp3.message = "成功";
        return tmp3;
    }


    public ResponseModel<InstrumentStore> GetKdInvData(string para, string enddate)
    {
        var jSetting = new JsonSerializerSettings();
        jSetting.NullValueHandling = NullValueHandling.Ignore;

        //  一 、获取商品数据
        int curentpage = 1;
        int totalpage = 1;
        List<Material> kdmat = new List<Material>();

        while (curentpage <= totalpage)
        {
            string urlmatrial = "http://api.kingdee.com/jdy/basedata/material_list?access_token=" + acctInfo.accessToken;
            string jsonmat = "{    " +
                "    \"search\":\"\",  " +
                "    \"enable\":\"-1\",  " +
                "    \"parent\":[],  " +
                "    \"selectfields\":\"refistrationnumber\",  " +
                "    \"starttime\":-1,  " +
                "    \"endtime\":-1,  " +
                "    \"page\":" + curentpage + ",  " +
                "    \"pagesize\":1000  " +
                "    }  ";

            string rtmat = postCustApiClient(urlmatrial, jsonmat);
            if (JObject.Parse(rtmat)["success"].Value<string>().ToUpper().Equals("TRUE"))
            {
                curentpage = (int)JObject.Parse(rtmat)["data"]["page"] + 1;
                totalpage = (int)JObject.Parse(rtmat)["data"]["totalpage"];
                string oo = JObject.Parse(rtmat)["data"]["rows"].ToString();
                kdmat.AddRange(JsonConvert.DeserializeObject<List<Material>>(oo, jSetting));
            }

        }



        //  二 、获取即时库存数据
        string url = "http://api.kingdee.com/jdy/inv/inv_list?access_token=" + acctInfo.accessToken;
        string jsonprepram = "{   " +
                        "    \"auxpropids\":[],   " +
                        "    \"spids\":\"\",   " +
                        "    \"stockids\":[],   " +
                        "    \"materialids\":[],   " +
                        "    \"begindate\":\"\",   " +
                        "    \"expiredate\":\"\",   " +
                        "    \"include_batch_kfperiod\":true,   " +
                        "    \"include_package\":false,   " +
                        "    \"totalfields\":[\"stockid,materialid\"]   " +
                        "    }   ";

        string rt = postCustApiClient(url, jsonprepram);
        List<KDInstrumentInv> kdpresale =new  List<KDInstrumentInv>();
        if (JObject.Parse(rt)["success"].Value<string>().ToUpper().Equals("TRUE"))
        {
            string oo = JObject.Parse(rt)["data"]["rows"].ToString();
            kdpresale = JsonConvert.DeserializeObject<List<KDInstrumentInv>>(oo, jSetting);
        }






        // 二 、 获取单据明细 并组装数据
        List<InstrumentStore> islist = new List<InstrumentStore>();
        foreach (KDInstrumentInv kdise in kdpresale)
        {
            Material mt = (Material)kdmat.Where(t => t.number.Equals(kdise.materialid_number)).FirstOrDefault();

            islist.Add(new InstrumentStore()
            {
                INSTRUMENT_CODE = kdise.materialid_number,
                INSTRUMENT_NAME = kdise.materialid_name,
                REGIST_NO = mt.refistrationnumber,
                BATCH_NO = kdise.batchno,
                VALIDITY = kdise.validdate,
                PRODUCER = mt.brandid_name,
                SPEC = kdise.materialid_model,
                UNIT = mt.baseunitid_name,
                QTY = kdise.qty,
                DESTROY_NO = ""
            });

        }


        ResponseModel<InstrumentStore> tmp3 = new ResponseModel<InstrumentStore>(false, "失败");
        tmp3.data = islist;
        tmp3.success = true;
        tmp3.message = "成功";
        return tmp3;

    }

    public  ResponseModel<InstrumentStock> GetKdPurData(string para, string enddate)
    {
        var jSetting = new JsonSerializerSettings();
        jSetting.NullValueHandling = NullValueHandling.Ignore;


        //  一 、获取预查询数据
        string jsonprepram = " {  " +
        "     \"deptid\":[], " +
        "     \"empid\":[], " +
        "     \"billstatus\":\"\", " +
        "     \"startdate\" : \"" + enddate + "\", " +
        "     \"enddate\" : \"" + enddate + "\", " +
        "     \"starttime\":-1, " +
        "     \"endtime\":-1, " +
        "     \"startbilldate\":\"\", " +
        "     \"endbilldate\":\"\", " +
        "     \"selectfields\":\"\", " +
        "     \"search\":\"\", " +
        "     \"page\":1, " +
        "     \"pagesize\":1000 " +
        " } ";

        string rt = postCustApiClient("http://api.kingdee.com/jdy/pur/pur_inbound_list?access_token=" + acctInfo.accessToken, jsonprepram);
        List<KDPreInstrumentPur> kdpresale = new List<KDPreInstrumentPur>();
        if (JObject.Parse(rt)["success"].Value<string>().ToUpper().Equals("TRUE"))
        {
            string oo = JObject.Parse(rt)["data"]["rows"].ToString();
            kdpresale = JsonConvert.DeserializeObject<List<KDPreInstrumentPur>>(oo, jSetting);
        }






        // 二 、 获取单据明细 并组装数据
        List<InstrumentStock> islist = new List<InstrumentStock>();
        foreach (KDPreInstrumentPur kdpis in kdpresale)
        {
            string jsonpram = " {\"number\":\"" + kdpis.billno + "\"}";
            string oom = postCustApiClient("http://api.kingdee.com/jdy/pur/pur_inbound_detail?access_token=" + acctInfo.accessToken, jsonpram);
            string ooma = JObject.Parse(oom)["data"].ToString();
            KDInstrumentPur kdis = JsonConvert.DeserializeObject<KDInstrumentPur>(ooma, jSetting);

            foreach (KDInstrumentPurEntity kdise in kdis.material_entity)
            {
                islist.Add(new InstrumentStock()
                {
                    BUY_NO = kdis.billno,
                    PURCHASE_DATE = kdis.billdate,
                    INSTRUMENT_CODE = kdise.materialid_number,
                    INSTRUMENT_NAME = kdise.materialid_name,
                    REGIST_NO = kdise.materialid_refistrationnumber,
                    BATCH_NO = kdise.batchno,
                    VALIDITY = kdise.validdate,
                    PRODUCER = kdise.custom_entity_field__6__22ghpxiehohfly__materialid__custom_field__4__22dyhvyxiew4bixieq,
                    SPEC = kdise.materialid_model,
                    UNIT = kdise.unit_name,
                    QTY = kdise.qty,
                    PROVIDER_NAME = kdis.supplierid_name,
                    PROVIDER_CODE = kdpis.supplierid_number,
                    DESTROY_NO = ""
                });
            }
        }

        ResponseModel<InstrumentStock> tmp3 = new ResponseModel<InstrumentStock>(false, "失败");
        tmp3.data = islist;
        tmp3.success = true;
        tmp3.message = "成功";
        return tmp3;
    }


    /// <summary>
    /// V1调用方式，获取token都是一样的
    /// </summary>
    /// <param name="url"></param>
    /// <param name="jsonDate"></param>
    /// <returns></returns>
    private string postCustApiClient(string url, string jsonDate)
    {
        HttpClient _client = new HttpClient();
        _client.Timeout = TimeSpan.FromSeconds(10);

        //_client.DefaultRequestHeaders.Add("Content-Type","application/json");
        _client.DefaultRequestHeaders.Add("groupName", acctInfo.groupName);
        _client.DefaultRequestHeaders.Add("accountId", acctInfo.accountId);
        _client.DefaultRequestHeaders.Add("X-GW-Router-Addr", acctInfo.domain);


        string rt = "";
        //string content = JsonSerializer.Serialize(jsonDate);
        rt = _client.PostAsync(url, new StringContent(jsonDate, Encoding.UTF8, "application/json")).Result.Content.ReadAsStringAsync().Result;
        return rt;
    }


    /// <summary>
    /// 认证获取token
    /// </summary>
    /// <returns></returns>
    public bool initToken()
    {
        bool islogin = false;

        // 获取账套信息
        string rt = Execute(new APIRequest
        {
            Path = "/jdyconnector/app_management/push_app_authorize",
            Method = HttpMethod.Post,
            Params = new Dictionary<string, string>()
                {
                    {"outerInstanceId",outerInstanceId},

                }
        }).Result.Content.ReadAsStringAsync().Result;


        // 获取token
        if (200 == JObject.Parse(rt)["code"].Value<int>())
        {
            var jSetting = new JsonSerializerSettings
            {
                NullValueHandling = NullValueHandling.Ignore,
                MissingMemberHandling = MissingMemberHandling.Ignore
            };
            acctInfo = JsonConvert.DeserializeObject<AcctInfo>(JArray.Parse(JObject.Parse(rt)["data"].ToString()).First.ToString(), jSetting);

            // 获取授权信息
            string rt1 = Execute(new APIRequest
            {
                Path = "/jdyconnector/app_management/kingdee_auth_token",
                Method = HttpMethod.Get,
                Params = new Dictionary<string, string>()
                    {
                        {"app_key",acctInfo.appKey},
                        {"app_signature",HmacSHA256(acctInfo.appSecret, acctInfo.appKey)},
                    }
            }).Result.Content.ReadAsStringAsync().Result;

            if (0 == JObject.Parse(rt1)["errcode"].Value<int>())
            {
                acctInfo.accessToken = JObject.Parse(rt1)["data"]["access_token"].Value<string>();
                acctInfo.appToken = JObject.Parse(rt1)["data"]["app-token"].Value<string>();
                islogin = true;
            }
        }

        return islogin;
    }


    /// <summary>
    /// 发起请求
    /// </summary>
    /// <param name="request"></param>
    /// <returns></returns>
    public async Task<HttpResponseMessage> Execute(APIRequest request)
    {
        using (HttpClient _client = new HttpClient())
        {

            _client.Timeout = TimeSpan.FromSeconds(10);
            BuildRequestHeader(_client.DefaultRequestHeaders, request);


            var uri = "https://api.kingdee.com/"  + request.Path + "?" + GetQueryString(request);

            if (request.Method == HttpMethod.Post)
            {
                if (string.IsNullOrEmpty(request.File))
                {
                    //string rt = _client.PostAsync(requestUri, new StringContent(string.Empty, Encoding.UTF8, "application/json")).Result.Content.ReadAsStringAsync().Result;

                    var json = request.Body == null ? string.Empty : System.Text.Json.JsonSerializer.Serialize(request.Body);
                    return await _client.PostAsync(uri, new StringContent(json, Encoding.UTF8, request.ContentType));

                }
                else
                {
                    //文件上传
                    string boundary = $"----WebKitFormBoundary{DateTime.Now.Ticks:x}";
                    MultipartFormDataContent content = new MultipartFormDataContent(boundary);
                    if (!File.Exists(request.File))
                    {
                        throw new Exception($"路径不存在:{request.File}");
                    }

                    using (FileStream fStream = File.Open(request.File, FileMode.Open, FileAccess.Read))
                    {
                        content.Add(new StreamContent(fStream, (int)fStream.Length), "file", Path.GetFileName(request.File));
                        return await _client.PostAsync(uri, content);
                    }
                }
            }
            else if (request.Method == HttpMethod.Get)
            {
                return await _client.GetAsync(uri);
            }
            else if (request.Method == HttpMethod.Put)
            {
                var json = request.Body == null ? string.Empty : System.Text.Json.JsonSerializer.Serialize(request.Body);
                return await _client.PutAsync(uri, new StringContent(json, Encoding.UTF8, request.ContentType));
            }
            else if (request.Method == HttpMethod.Delete)
            {
                return await _client.DeleteAsync(uri);
            }
            else
            {
                throw new Exception($"Method:{request.Method} 参数不支持!");
            }

        }
    }


    private void BuildRequestHeader(HttpRequestHeaders headers, APIRequest request)
    {
        var api_nonce = new Random().Next(100000000, 999999999);
        var timeStamp = (int)(DateTime.Now.ToLocalTime() - DateTime.Parse("1970-01-01").ToLocalTime()).TotalSeconds;

        headers.Add("X-Api-Nonce", api_nonce.ToString());
        headers.Add("X-Api-Auth-Version", "2.0");
        headers.Add("X-Api-TimeStamp", timeStamp.ToString());
        headers.Add("X-Api-ClientID", ClientID);
        headers.Add("X-Api-Signature", GetSignature(request, api_nonce.ToString(), timeStamp.ToString()));
        headers.Add("X-Api-SignHeaders", "X-Api-Nonce,X-Api-TimeStamp");


        if (request.Header.Any())
        {
            foreach (var hk in request.Header.Keys)
            {
                headers.Add(hk, request.Header[hk]);
            }
        }
    }


    private string GetSignature(APIRequest request, string nonce, string timestamp)
    {

        var querySign = GetQueryString(request, true);

        StringBuilder sb = new StringBuilder();
        sb.Append(request.Method.ToString().ToUpper());
        sb.Append('\n');
        sb.Append(PathEncode(request.Path));
        sb.Append('\n');
        sb.Append(querySign);
        sb.Append('\n');
        sb.Append($"x-api-nonce:{nonce}");
        sb.Append('\n');
        sb.Append($"x-api-timestamp:{timestamp}");
        sb.Append('\n');
        return HmacSHA256(ClientSecret, sb.ToString());
    }

    //！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！

    // 原方法严重bug，没有考虑参数排序的问题，影响签名字符串。所以改了

    //！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！

    /* 
        参考https://open.jdy.com/#/files/api/detail?index=3&categrayId=5403e0fd6a5811eda819b759130d6d33&id=b5c26557da9d11ed8e36f9799578d2e1

        X-Api-Signature生成规则

        准备签名原文：
        请求方式（POST、GET、DELETE等均为大写）

        请求地址的path部分（示例：https://api.kingdee.com/jdyconnector/app_management/kingdee_auth_token，取红色部分，进行url编码）

        params请求参数（进行两次url编码（编码后字母应是大写，下同），加密时按参数名ASCII码升序顺序进行排序，实际请求参数顺序必须一致）

        headers请求参数（只需要x-api-nonce、x-api-timestamp参与加密，必须小写）

        拼接得到签名原文，每段签名字符拼接均需要换行符（x-api-timestamp时间戳后需要换行符）
    */


    /// <summary>
    /// 查询字符串部分拼接,区分一次加密参数和二次加密参数
    /// </summary>
    /// <param name="request"></param>
    /// <returns></returns>
    private static string GetQueryString(APIRequest request, bool doubleEncode = false)
    {
        var querySign = string.Empty;


        if (request.Params != null && request.Params.Keys != null)
        {
            // 先来个排序
            // 版权声明：本文为CSDN博主「heyuchang666」的原创文章，遵循CC 4.0 BY - SA版权协议，转载请附上原文出处链接及本声明。
            // 原文链接：https://blog.csdn.net/heyuchang666/article/details/49683349
            // foreach (KeyValuePair<string, int> kvp in lst)
            // foreach (var key in request.Params.Keys)

            List<KeyValuePair<string, string>> lst = new List<KeyValuePair<string, string>>(request.Params);
            lst.Sort(delegate (KeyValuePair<string, string> s1, KeyValuePair<string, string> s2)
            {
                return s1.Key.CompareTo(s2.Key);
            });

            foreach (KeyValuePair<string, string> item in lst)
            {
                if (doubleEncode)
                {
                    querySign = querySign
                            + $"&{item.Key}="
                            + HttpUtility.UrlEncode(HttpUtility.UrlEncode(item.Value));
                }
                else
                {
                    querySign = querySign
                            + $"&{item.Key}="
                            + HttpUtility.UrlEncode(item.Value);
                }
            }
            querySign = querySign.TrimStart('&');
        }

        return querySign;
    }


    /// <summary>
    /// Url Path部分Url编码
    /// </summary>
    /// <param name="path"></param>
    /// <returns></returns>
    private string PathEncode(string path) => HttpUtility.UrlEncode("/" + path.TrimStart('/'))
                .Replace("%2f", "%2F")
                .Replace("%2d", "%2D")
                .Replace("(", "%28")
                .Replace(")", "%29");

    /// <summary>
    /// 实现HMAC SHA256签名算法
    /// </summary>
    /// <param name="secret">加密秘钥</param>
    /// <param name="signContent">加密内容</param>
    /// <returns></returns>
    public string HmacSHA256(string secret, string signContent)
    {
        string signRet = string.Empty;
        using (HMACSHA256 mac = new HMACSHA256(Encoding.UTF8.GetBytes(secret)))
        {
            byte[] hash = mac.ComputeHash(Encoding.UTF8.GetBytes(signContent));
            signRet = ToHexString(hash);
        }
        var hexBytes = Encoding.UTF8.GetBytes(signRet);
        return Convert.ToBase64String(hexBytes);
    }

    /// <summary>
    ///  转16进制
    /// </summary>
    /// <param name="bytes"></param>
    /// <returns></returns>
    protected string ToHexString(byte[] bytes)
    {
        string hexString = string.Empty;
        if (bytes != null)
        {
            StringBuilder strB = new StringBuilder();
            foreach (byte b in bytes)
            {
                strB.AppendFormat("{0:x2}", b);
            }
            hexString = strB.ToString();
        }
        return hexString;
    }


}